// 
//  Author:
//    TachyonXV tachyonxv@psiforum.co.uk
// 
//  Copyright (c) 2011, TachyonXV
// 
//  All rights reserved.
// 
//  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
// 
//     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in
//       the documentation and/or other materials provided with the distribution.
//     * Neither the name of the [ORGANIZATION] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
// 
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
//  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
using HttpServer;
using HttpServer.Headers;
using HttpServer.Logging;
using HttpServer.Modules;
using System;
using System.Text;
using ZMQ;
using System.IO;

namespace CrawlRemix
{
    /// <summary>
    /// 
    /// </summary>
    public class TachModule : IModule, IDisposable
	{
		/// <summary>
		/// 
		/// </summary>
        private ILogger _logger = LogFactory.CreateLogger(typeof(TachModule));

        /// <summary>
        /// 
        /// </summary>
        private Socket cmdSkt;

        /// <summary>
        /// 
        /// </summary>
        private Socket pageSkt;

        /// <summary>
        /// 
        /// </summary>
        private Socket msgsSkt;

        /// <summary>
        /// 
        /// </summary>
        private Socket stateSkt;

        /// <summary>
        /// ...
        /// </summary>
        private TachModule()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TachModule"/> class.
        /// </summary>
        /// <param name="ctx">The CTX.</param>
		public TachModule (Context ctx)
		{
            const string address = "inproc://tachbot";

            cmdSkt = ctx.Socket(SocketType.REQ);
            cmdSkt.Connect(address);

            pageSkt = ctx.Socket(SocketType.REQ);
            pageSkt.Connect(address);

            msgsSkt = ctx.Socket(SocketType.REQ);
            msgsSkt.Connect(address);

            stateSkt = ctx.Socket(SocketType.REQ);
            stateSkt.Connect(address);
		}

        /// <summary>
        /// Process a request.
        /// </summary>
        /// <param name="context">Request information</param>
        /// <returns>
        /// What to do next.
        /// </returns>
        ProcessingResult IModule.Process(RequestContext context)
		{
            IResponse response = context.Response;
            var sb = new StringBuilder();
            Uri uri = context.Request.Uri;
            byte[] buffer;
            if (uri.LocalPath == "/getpages")
            {
                ContentTypeHeader header = new ContentTypeHeader("application/xml");
                response.ContentType = header;
                response.Reason = "OK";
                cmdSkt.Send("pages", Encoding.Unicode);
                buffer = UTF8Encoding.UTF8.GetBytes(cmdSkt.Recv(Encoding.Unicode));
                //response.Add(new DateHeader("Last-Modified", DateTime.UtcNow));
                response.Add(new StringHeader("Cache-Control", "no-cache"));
                response.ContentLength.Value = buffer.Length;
            }
            else if (uri.LocalPath == "/getcategories")
            {
                ContentTypeHeader header = new ContentTypeHeader("application/xml");
                response.ContentType = header;
                response.Reason = "OK";
                cmdSkt.Send("categories", Encoding.Unicode);
                buffer = UTF8Encoding.UTF8.GetBytes(cmdSkt.Recv(Encoding.Unicode));
                //response.Add(new DateHeader("Last-Modified", DateTime.UtcNow));
                response.Add(new StringHeader("Cache-Control", "no-cache"));
                response.ContentLength.Value = buffer.Length;
            }
            else if (uri.LocalPath == "/gettemplates")
            {
                ContentTypeHeader header = new ContentTypeHeader("application/xml");
                response.ContentType = header;
                response.Reason = "OK";
                cmdSkt.Send("templates", Encoding.Unicode);
                buffer = UTF8Encoding.UTF8.GetBytes(cmdSkt.Recv(Encoding.Unicode));
                //response.Add(new DateHeader("Last-Modified", DateTime.UtcNow));
                response.Add(new StringHeader("Cache-Control", "no-cache"));
                response.ContentLength.Value = buffer.Length;
            }
            else if (uri.LocalPath == "/resetpages")
            {
                ContentTypeHeader header = new ContentTypeHeader("application/xml");
                response.ContentType = header;
                response.Reason = "OK";
                cmdSkt.Send("resetpages", Encoding.Unicode);
                buffer = UTF8Encoding.UTF8.GetBytes(cmdSkt.Recv(Encoding.Unicode));
                //response.Add(new DateHeader("Last-Modified", DateTime.UtcNow));
                response.Add(new StringHeader("Cache-Control", "no-cache"));
                response.ContentLength.Value = buffer.Length;
            }
            else if (uri.LocalPath == "/parsepages")
            {
                ContentTypeHeader header = new ContentTypeHeader("application/xml");
                response.ContentType = header;
                response.Reason = "OK";
                cmdSkt.Send("parsepages", Encoding.Unicode);
                buffer = UTF8Encoding.UTF8.GetBytes(cmdSkt.Recv(Encoding.Unicode));
                //response.Add(new DateHeader("Last-Modified", DateTime.UtcNow));
                response.Add(new StringHeader("Cache-Control", "no-cache"));
                response.ContentLength.Value = buffer.Length;
            }
            else if (uri.LocalPath == "/getstate")
            {
                ContentTypeHeader header = new ContentTypeHeader("application/xml");
                response.ContentType = header;
                response.Reason = "OK";
                stateSkt.Send("getstate", Encoding.Unicode);
                buffer = UTF8Encoding.UTF8.GetBytes(stateSkt.Recv(Encoding.Unicode));
                //response.Add(new DateHeader("Last-Modified", DateTime.UtcNow));
                response.Add(new StringHeader("Cache-Control", "no-cache"));
                response.ContentLength.Value = buffer.Length;
            }
            else if (uri.LocalPath == "/getmessages")
            {
                ContentTypeHeader header = new ContentTypeHeader("application/xml");
                response.ContentType = header;
                response.Reason = "OK";
                string update = context.Request.Parameters["update"];
                msgsSkt.Send("getmessages:" + update, Encoding.Unicode);
                buffer = UTF8Encoding.UTF8.GetBytes(msgsSkt.Recv(Encoding.Unicode));
                //response.Add(new DateHeader("Last-Modified", DateTime.UtcNow));
                response.Add(new StringHeader("Cache-Control", "no-cache"));
                response.ContentLength.Value = buffer.Length;
            }
            else if (uri.LocalPath == "/getpagelist")
            {
                ContentTypeHeader header = new ContentTypeHeader("application/xml");
                response.ContentType = header;
                response.Reason = "OK";
                string update = context.Request.Parameters["update"];
                pageSkt.Send("getpagelist:" + update, Encoding.Unicode);
                buffer = UTF8Encoding.UTF8.GetBytes(pageSkt.Recv(Encoding.Unicode));
                //response.Add(new DateHeader("Last-Modified", DateTime.UtcNow));
                response.Add(new StringHeader("Cache-Control", "no-cache"));
                response.ContentLength.Value = buffer.Length;
            }
            else if (uri.LocalPath == "/prototype.js")
            {
                ContentTypeHeader header = new ContentTypeHeader("text/javascript");
                response.ContentType = header;
                response.Reason = "OK";
                StreamReader reader = new StreamReader("prototype.js");
                buffer = UTF8Encoding.UTF8.GetBytes(reader.ReadToEnd());
                reader.Close();
                reader.Dispose();
                response.Add(new DateHeader("Last-Modified", new DateTime(2011, 8, 29)));
                response.ContentLength.Value = buffer.Length;
            }
            else if (uri.LocalPath == "/index.html")
            {
                ContentTypeHeader header = new ContentTypeHeader("text/html");
                response.ContentType = header;
                response.Reason = "OK";
                buffer = Encoding.UTF8.GetBytes(MkPageReal());
                response.Add(new DateHeader("Last-Modified", DateTime.UtcNow));
                response.ContentLength.Value = buffer.Length;
            }
            else
            {
                ContentTypeHeader header = new ContentTypeHeader("text/html");
                response.ContentType = header;
                response.Reason = "OK";
                buffer = Encoding.UTF8.GetBytes(MkPageFake());
                response.Add(new DateHeader("Last-Modified", DateTime.UtcNow));
                response.ContentLength.Value = buffer.Length;
            }

			sb.AppendFormat("{0} {1} {2}\r\n", response.HttpVersion, (int) response.Status, response.Reason);
			
			// go through all property headers.
			sb.AppendFormat("{0}: {1}\r\n", response.ContentType.Name, response.ContentType);
			sb.AppendFormat("{0}: {1}\r\n", response.ContentLength.Name, response.ContentLength);
			sb.AppendFormat("{0}: {1}\r\n", response.Connection.Name, response.Connection);
            foreach (IHeader header1 in response)
            {
                sb.AppendFormat("{0}: {1}\r\n", header1.Name, header1);
            }

    		sb.Append("\r\n");
    		Send(context.HttpContext, sb.ToString(), response.Encoding);
    		context.HttpContext.Stream.Write(buffer, 0, buffer.Length);
            
            // Console.WriteLine("done");
			return ProcessingResult.Abort;
		}

        /// <summary>
        /// Sends the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="data">The data.</param>
        /// <param name="encoding">The encoding.</param>
		public void Send(IHttpContext context, string data, Encoding encoding)
        {
			try
			{
				byte[] buffer = encoding.GetBytes(data);
				context.Stream.Write(buffer, 0, buffer.Length);
			}
			catch(System.Exception err)
			{
				_logger.Error("Failed to send data through context stream.", err);
			}
        }

        /// <summary>
        /// Mks the page.
        /// </summary>
        /// <returns></returns>
        private string MkPageReal()
        {
            return Strings.Html +
                "<script type=\"text/javascript\">\n" +
                "function getPages() {\n" +
                "new Ajax.Request('/getpages', {\n" +
                "method: 'get',\n" +
                "onSuccess: function(transport) {\n" +
                "var notice = $('notice');\n" +
                "$('status').innerHTML = transport.responseText;\n" +
                "}\n" +
                "});\n" +
                "}\n" +
                "function getCategories() {\n" +
                "new Ajax.Request('/getcategories', {\n" +
                "method: 'get',\n" +
                "onSuccess: function(transport) {\n" +
                "var notice = $('notice');\n" +
                "$('status').innerHTML = transport.responseText;\n" +
                "}\n" +
                "});\n" +
                "}\n" +
                "function getTemplates() {\n" +
                "new Ajax.Request('/gettemplates', {\n" +
                "method: 'get',\n" +
                "onSuccess: function(transport) {\n" +
                "var notice = $('notice');\n" +
                "$('status').innerHTML = transport.responseText;\n" +
                "}\n" +
                "});\n" +
                "}\n" +
                "function parsePages() {\n" +
                "new Ajax.Request('/parsepages', {\n" +
                "method: 'get',\n" +
                "onSuccess: function(transport) {\n" +
                "var notice = $('notice');\n" +
                "$('status').innerHTML = transport.responseText;\n" +
                "}\n" +
                "});\n" +
                "}\n" +
                "function resetPages() {\n" +
                "new Ajax.Request('/resetpages', {\n" +
                "method: 'get',\n" +
                "onSuccess: function(transport) {\n" +
                "var notice = $('notice');\n" +
                "$('status').innerHTML = transport.responseText;\n" +
                "}\n" +
                "});\n" +
                "}\n" +
                
                "function getPagelist() {\n" +
                "  var upda = $('updat');\n" +
                "  if (upda != null)\n" +
                "  {\n" +
                "  var updat = upda.value;\n" +
                "  var params = {\"update\":updat};\n" +
                "  var a = new Ajax.Request('/getpagelist', {\n" +
                "    method: 'get',\n" +
                "    parameters: params,\n" +
                "    onSuccess: function(transport)\n" +
                "    {\n" +
                "      var ie = Prototype.Browser.IE ? 1 : 3;\n" +
                "      var xmll = transport.responseXML.documentElement.childNodes[ie];\n" +
                "      if (Prototype.Browser.IE)\n" +
                "      {\n" +
                "        if(xmll.text != '')\n" +
                "        {\n" +
                "          $('pages').innerHTML = xmll.childNodes[0].xml;\n" +
                "        }\n" +
                "      }\n" +
                "      else\n" +
                "      {\n" +
                "        if(xmll.textContent != '')\n" +
                "        {\n" +
                "          $('pages').innerHTML = (new XMLSerializer()).serializeToString(xmll.childNodes[0]);\n" +
                "        }\n" +
                "      }\n" +
                "    }\n" +
                "  });\n" +
                "  }\n" +
                "}\n" +

                "function getMessages()\n" +
                "{\n" +
                "  var upda = $('updat');\n" +
                "  if (upda != null)\n" +
                "  {\n" +
                "  var updat = upda.value;\n" +
                "  var params = {\"update\":updat};\n" +
                "  var a = new Ajax.Request('/getmessages', {\n" +
                "    method: 'get',\n" +
                "    parameters: params,\n" +
                "    onSuccess: function(transport)\n" +
                "    {\n" +
                "      var ie = Prototype.Browser.IE ? 1 : 3;\n" +
                "      var xmll = transport.responseXML.documentElement.childNodes[ie];\n" +
                "      if (Prototype.Browser.IE)\n" +
                "      {\n" +
                "        if(xmll.text != '')\n" +
                "        {\n" +
                "          $('messages').innerHTML = xmll.childNodes[0].xml;\n" +
                "        }\n" +
                "      }\n" +
                "      else\n" +
                "      {\n" +
                "        if(xmll.textContent != '')\n" +
                "        {\n" +
                "          $('messages').innerHTML = (new XMLSerializer()).serializeToString(xmll.childNodes[0]);\n" +
                "        }\n" +
                "      }\n" +
                "      if (Prototype.Browser.IE)\n" +
                "        $('updat').value = transport.responseXML.documentElement.childNodes[0].childNodes[0].text;\n" +
                "      else\n" +
                "        $('updat').value = transport.responseXML.documentElement.childNodes[1].childNodes[0].textContent;\n" +
                "    }\n" +
                "  });\n" +
                "  }\n" +
                "}\n" +

                "setInterval('clock()',5000);\n" +
                "function clock()\n" +
                "{\n" +
                "  getPagelist();\n" +
                "  getMessages();\n" +
                "}\n" +

                "setInterval('state()',3000);\n" +
                "function state()\n" +
                "{\n" +
                "  new Ajax.Request('/getstate', {\n" +
                "    method: 'get',\n" +
                "    onSuccess: function(transport) {\n" +
                "      $('state').innerHTML = transport.responseText;\n" +
                "    }\n" +
                "  });\n" +
                "}\n" +

                "</script>\n" +
                Strings.Css +
                Strings.Start +
                Strings.Body +
                Strings.Status +
                Strings.Main +
                Strings.End;
        }

        /// <summary>
        /// Mks the page.
        /// </summary>
        /// <returns></returns>
		private string MkPageFake()
		{
            return Strings.Html +
                Strings.Css +
                Strings.Start +
                Strings.Lolwut +
                Strings.End;
		}

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        void IDisposable.Dispose()
        {
            cmdSkt.Dispose();
            pageSkt.Dispose();
            msgsSkt.Dispose();
            stateSkt.Dispose();
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        internal void Close()
        {
            (this as IDisposable).Dispose();
        }
    }
}

